"""
AI Agent Pro - 计算工具
"""

import ast
import math
import statistics
from typing import Any, Dict, List, Union
import numpy as np
import pandas as pd

from app.core.logging import get_logger
from app.tools.base import BaseTool, ToolResult, ToolSchema, ToolParameter, ToolType

logger = get_logger(__name__)


class MathCalculatorTool(BaseTool):
    """数学计算器工具"""
    
    def get_schema(self) -> ToolSchema:
        return ToolSchema(
            name="math_calculator",
            description="执行数学表达式计算，支持基本运算、三角函数、对数等",
            tool_type=ToolType.COMPUTE,
            parameters=[
                ToolParameter(
                    name="expression",
                    type="string",
                    description="数学表达式，如 '2 + 3 * 4' 或 'sin(pi/2)'",
                    required=True
                ),
                ToolParameter(
                    name="precision",
                    type="integer",
                    description="结果精度（小数位数）",
                    required=False,
                    default=6,
                    min_value=0,
                    max_value=15
                )
            ],
            examples=[
                {
                    "expression": "2 + 3 * 4",
                    "precision": 2
                },
                {
                    "expression": "sin(pi/2) + cos(0)",
                    "precision": 4
                },
                {
                    "expression": "sqrt(16) + log(e)",
                    "precision": 3
                }
            ],
            tags=["数学", "计算", "公式"],
            timeout=10
        )
    
    async def _execute(self, **kwargs) -> ToolResult:
        expression = kwargs.get("expression")
        precision = kwargs.get("precision", 6)
        
        try:
            # 安全的数学计算环境
            safe_dict = {
                # 基本运算符号已内置
                # 数学常数
                "pi": math.pi,
                "e": math.e,
                "tau": math.tau,
                "inf": math.inf,
                "nan": math.nan,
                
                # 基本数学函数
                "abs": abs,
                "round": round,
                "pow": pow,
                "sqrt": math.sqrt,
                "ceil": math.ceil,
                "floor": math.floor,
                
                # 三角函数
                "sin": math.sin,
                "cos": math.cos,
                "tan": math.tan,
                "asin": math.asin,
                "acos": math.acos,
                "atan": math.atan,
                "atan2": math.atan2,
                "sinh": math.sinh,
                "cosh": math.cosh,
                "tanh": math.tanh,
                
                # 对数函数
                "log": math.log,
                "log10": math.log10,
                "log2": math.log2,
                "exp": math.exp,
                
                # 其他数学函数
                "degrees": math.degrees,
                "radians": math.radians,
                "factorial": math.factorial,
                "gcd": math.gcd,
                "lcm": math.lcm if hasattr(math, 'lcm') else lambda a, b: abs(a * b) // math.gcd(a, b),
                
                # 统计函数
                "sum": sum,
                "max": max,
                "min": min,
            }
            
            # 解析并执行表达式
            # 使用 ast.parse 确保安全性
            try:
                # 先用 eval 进行计算（在受限环境中）
                result = eval(expression, {"__builtins__": {}}, safe_dict)
            except Exception as e:
                # 如果 eval 失败，尝试使用 ast 解析
                node = ast.parse(expression, mode='eval')
                result = self._eval_ast_node(node.body, safe_dict)
            
            # 格式化结果
            if isinstance(result, (int, float)):
                if precision == 0:
                    formatted_result = int(result) if result.is_integer() else result
                else:
                    formatted_result = round(result, precision)
            else:
                formatted_result = result
            
            return ToolResult(
                success=True,
                data={
                    "expression": expression,
                    "result": formatted_result,
                    "result_type": type(formatted_result).__name__
                },
                metadata={
                    "precision": precision,
                    "original_result": result
                }
            )
            
        except ZeroDivisionError:
            return ToolResult(
                success=False,
                error="除零错误：表达式中存在除以零的操作"
            )
        except ValueError as e:
            return ToolResult(
                success=False,
                error=f"数值错误：{str(e)}"
            )
        except SyntaxError as e:
            return ToolResult(
                success=False,
                error=f"语法错误：表达式格式不正确 - {str(e)}"
            )
        except Exception as e:
            logger.error(f"数学计算失败: {str(e)}")
            return ToolResult(
                success=False,
                error=f"计算失败：{str(e)}"
            )
    
    def _eval_ast_node(self, node, safe_dict):
        """安全地评估 AST 节点"""
        if isinstance(node, ast.Constant):
            return node.value
        elif isinstance(node, ast.Name):
            return safe_dict.get(node.id, 0)
        elif isinstance(node, ast.BinOp):
            left = self._eval_ast_node(node.left, safe_dict)
            right = self._eval_ast_node(node.right, safe_dict)
            return self._apply_binop(node.op, left, right)
        elif isinstance(node, ast.UnaryOp):
            operand = self._eval_ast_node(node.operand, safe_dict)
            return self._apply_unaryop(node.op, operand)
        elif isinstance(node, ast.Call):
            func_name = node.func.id if isinstance(node.func, ast.Name) else None
            if func_name in safe_dict and callable(safe_dict[func_name]):
                args = [self._eval_ast_node(arg, safe_dict) for arg in node.args]
                return safe_dict[func_name](*args)
        
        raise ValueError(f"不支持的表达式节点: {type(node)}")
    
    def _apply_binop(self, op, left, right):
        """应用二元运算符"""
        if isinstance(op, ast.Add):
            return left + right
        elif isinstance(op, ast.Sub):
            return left - right
        elif isinstance(op, ast.Mult):
            return left * right
        elif isinstance(op, ast.Div):
            return left / right
        elif isinstance(op, ast.Pow):
            return left ** right
        elif isinstance(op, ast.Mod):
            return left % right
        elif isinstance(op, ast.FloorDiv):
            return left // right
        else:
            raise ValueError(f"不支持的二元运算符: {type(op)}")
    
    def _apply_unaryop(self, op, operand):
        """应用一元运算符"""
        if isinstance(op, ast.UAdd):
            return +operand
        elif isinstance(op, ast.USub):
            return -operand
        else:
            raise ValueError(f"不支持的一元运算符: {type(op)}")


class StatisticsCalculatorTool(BaseTool):
    """统计计算工具"""
    
    def get_schema(self) -> ToolSchema:
        return ToolSchema(
            name="statistics_calculator",
            description="对数值数据进行统计分析，计算均值、方差、标准差等统计指标",
            tool_type=ToolType.COMPUTE,
            parameters=[
                ToolParameter(
                    name="data",
                    type="array",
                    description="数值数组",
                    required=True
                ),
                ToolParameter(
                    name="calculations",
                    type="array",
                    description="要计算的统计指标",
                    required=False,
                    default=["mean", "median", "std", "var"]
                )
            ],
            examples=[
                {
                    "data": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
                    "calculations": ["mean", "median", "std", "min", "max"]
                }
            ],
            tags=["统计", "数据分析", "数学"],
            timeout=15
        )
    
    async def _execute(self, **kwargs) -> ToolResult:
        data = kwargs.get("data", [])
        calculations = kwargs.get("calculations", ["mean", "median", "std", "var"])
        
        try:
            # 验证数据
            if not data:
                return ToolResult(
                    success=False,
                    error="数据不能为空"
                )
            
            # 确保所有数据都是数值
            numeric_data = []
            for item in data:
                try:
                    numeric_data.append(float(item))
                except (ValueError, TypeError):
                    return ToolResult(
                        success=False,
                        error=f"数据中包含非数值项: {item}"
                    )
            
            if not numeric_data:
                return ToolResult(
                    success=False,
                    error="没有有效的数值数据"
                )
            
            # 计算统计指标
            results = {}
            
            for calc in calculations:
                try:
                    if calc == "mean":
                        results["mean"] = statistics.mean(numeric_data)
                    elif calc == "median":
                        results["median"] = statistics.median(numeric_data)
                    elif calc == "mode":
                        try:
                            results["mode"] = statistics.mode(numeric_data)
                        except statistics.StatisticsError:
                            results["mode"] = "无唯一众数"
                    elif calc == "std":
                        results["std"] = statistics.stdev(numeric_data) if len(numeric_data) > 1 else 0
                    elif calc == "var":
                        results["variance"] = statistics.variance(numeric_data) if len(numeric_data) > 1 else 0
                    elif calc == "min":
                        results["min"] = min(numeric_data)
                    elif calc == "max":
                        results["max"] = max(numeric_data)
                    elif calc == "range":
                        results["range"] = max(numeric_data) - min(numeric_data)
                    elif calc == "sum":
                        results["sum"] = sum(numeric_data)
                    elif calc == "count":
                        results["count"] = len(numeric_data)
                    elif calc == "q1":
                        results["q1"] = statistics.quantiles(numeric_data, n=4)[0]
                    elif calc == "q3":
                        results["q3"] = statistics.quantiles(numeric_data, n=4)[2]
                    elif calc == "iqr":
                        quantiles = statistics.quantiles(numeric_data, n=4)
                        results["iqr"] = quantiles[2] - quantiles[0]
                    else:
                        logger.warning(f"未知的统计计算类型: {calc}")
                
                except Exception as e:
                    logger.warning(f"计算 {calc} 时出错: {str(e)}")
                    results[calc] = f"计算错误: {str(e)}"
            
            return ToolResult(
                success=True,
                data={
                    "statistics": results,
                    "data_summary": {
                        "count": len(numeric_data),
                        "data_type": "numeric",
                        "sample_values": numeric_data[:5] if len(numeric_data) > 5 else numeric_data
                    }
                },
                metadata={
                    "original_data_length": len(data),
                    "processed_data_length": len(numeric_data),
                    "requested_calculations": calculations
                }
            )
            
        except Exception as e:
            logger.error(f"统计计算失败: {str(e)}")
            return ToolResult(
                success=False,
                error=f"统计计算失败: {str(e)}"
            )


class UnitConverterTool(BaseTool):
    """单位转换工具"""
    
    def get_schema(self) -> ToolSchema:
        return ToolSchema(
            name="unit_converter",
            description="在不同单位之间进行转换，支持长度、重量、温度、面积、体积等",
            tool_type=ToolType.COMPUTE,
            parameters=[
                ToolParameter(
                    name="value",
                    type="number",
                    description="要转换的数值",
                    required=True
                ),
                ToolParameter(
                    name="from_unit",
                    type="string",
                    description="源单位",
                    required=True
                ),
                ToolParameter(
                    name="to_unit",
                    type="string",
                    description="目标单位",
                    required=True
                ),
                ToolParameter(
                    name="category",
                    type="string",
                    description="单位类别",
                    required=False,
                    default="auto",
                    enum=["auto", "length", "weight", "temperature", "area", "volume", "time"]
                )
            ],
            examples=[
                {
                    "value": 100,
                    "from_unit": "cm",
                    "to_unit": "m",
                    "category": "length"
                },
                {
                    "value": 32,
                    "from_unit": "fahrenheit",
                    "to_unit": "celsius",
                    "category": "temperature"
                }
            ],
            tags=["转换", "单位", "计算"],
            timeout=5
        )
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # 定义转换表（以基础单位为基准）
        self.conversions = {
            "length": {
                # 基础单位：米 (m)
                "mm": 0.001,
                "cm": 0.01,
                "m": 1.0,
                "km": 1000.0,
                "inch": 0.0254,
                "ft": 0.3048,
                "foot": 0.3048,
                "yard": 0.9144,
                "mile": 1609.344,
            },
            "weight": {
                # 基础单位：克 (g)
                "mg": 0.001,
                "g": 1.0,
                "kg": 1000.0,
                "ton": 1000000.0,
                "lb": 453.592,
                "pound": 453.592,
                "oz": 28.3495,
                "ounce": 28.3495,
            },
            "area": {
                # 基础单位：平方米 (m²)
                "mm2": 0.000001,
                "cm2": 0.0001,
                "m2": 1.0,
                "km2": 1000000.0,
                "hectare": 10000.0,
                "acre": 4046.86,
            },
            "volume": {
                # 基础单位：升 (L)
                "ml": 0.001,
                "l": 1.0,
                "liter": 1.0,
                "m3": 1000.0,
                "gallon": 3.78541,
                "quart": 0.946353,
                "pint": 0.473176,
            },
            "time": {
                # 基础单位：秒 (s)
                "ms": 0.001,
                "s": 1.0,
                "second": 1.0,
                "min": 60.0,
                "minute": 60.0,
                "h": 3600.0,
                "hour": 3600.0,
                "day": 86400.0,
                "week": 604800.0,
                "month": 2592000.0,  # 30天
                "year": 31536000.0,  # 365天
            }
        }
    
    async def _execute(self, **kwargs) -> ToolResult:
        value = kwargs.get("value")
        from_unit = kwargs.get("from_unit", "").lower()
        to_unit = kwargs.get("to_unit", "").lower()
        category = kwargs.get("category", "auto")
        
        try:
            # 特殊处理温度转换
            if self._is_temperature_unit(from_unit) or self._is_temperature_unit(to_unit):
                result = self._convert_temperature(value, from_unit, to_unit)
                return ToolResult(
                    success=True,
                    data={
                        "original_value": value,
                        "original_unit": from_unit,
                        "converted_value": result,
                        "converted_unit": to_unit,
                        "conversion_formula": self._get_temperature_formula(from_unit, to_unit)
                    },
                    metadata={
                        "category": "temperature",
                        "conversion_type": "temperature"
                    }
                )
            
            # 自动检测类别
            if category == "auto":
                category = self._detect_category(from_unit, to_unit)
            
            if not category:
                return ToolResult(
                    success=False,
                    error=f"无法识别单位类别，请指定类别或检查单位名称"
                )
            
            # 获取转换表
            conversion_table = self.conversions.get(category)
            if not conversion_table:
                return ToolResult(
                    success=False,
                    error=f"不支持的单位类别: {category}"
                )
            
            # 检查单位是否存在
            if from_unit not in conversion_table:
                return ToolResult(
                    success=False,
                    error=f"不支持的源单位: {from_unit} (类别: {category})"
                )
            
            if to_unit not in conversion_table:
                return ToolResult(
                    success=False,
                    error=f"不支持的目标单位: {to_unit} (类别: {category})"
                )
            
            # 执行转换
            # 先转换为基础单位，再转换为目标单位
            base_value = value * conversion_table[from_unit]
            result = base_value / conversion_table[to_unit]
            
            return ToolResult(
                success=True,
                data={
                    "original_value": value,
                    "original_unit": from_unit,
                    "converted_value": round(result, 10),  # 避免浮点精度问题
                    "converted_unit": to_unit,
                    "conversion_factor": conversion_table[to_unit] / conversion_table[from_unit]
                },
                metadata={
                    "category": category,
                    "base_unit_value": base_value
                }
            )
            
        except Exception as e:
            logger.error(f"单位转换失败: {str(e)}")
            return ToolResult(
                success=False,
                error=f"单位转换失败: {str(e)}"
            )
    
    def _is_temperature_unit(self, unit: str) -> bool:
        """检查是否为温度单位"""
        temp_units = ["celsius", "fahrenheit", "kelvin", "c", "f", "k"]
        return unit.lower() in temp_units
    
    def _convert_temperature(self, value: float, from_unit: str, to_unit: str) -> float:
        """温度转换"""
        from_unit = from_unit.lower()
        to_unit = to_unit.lower()
        
        # 统一单位名称
        unit_mapping = {
            "c": "celsius",
            "f": "fahrenheit", 
            "k": "kelvin"
        }
        
        from_unit = unit_mapping.get(from_unit, from_unit)
        to_unit = unit_mapping.get(to_unit, to_unit)
        
        # 先转换为摄氏度
        if from_unit == "celsius":
            celsius = value
        elif from_unit == "fahrenheit":
            celsius = (value - 32) * 5/9
        elif from_unit == "kelvin":
            celsius = value - 273.15
        else:
            raise ValueError(f"不支持的温度单位: {from_unit}")
        
        # 从摄氏度转换为目标单位
        if to_unit == "celsius":
            return celsius
        elif to_unit == "fahrenheit":
            return celsius * 9/5 + 32
        elif to_unit == "kelvin":
            return celsius + 273.15
        else:
            raise ValueError(f"不支持的温度单位: {to_unit}")
    
    def _get_temperature_formula(self, from_unit: str, to_unit: str) -> str:
        """获取温度转换公式"""
        formulas = {
            ("celsius", "fahrenheit"): "F = C × 9/5 + 32",
            ("fahrenheit", "celsius"): "C = (F - 32) × 5/9",
            ("celsius", "kelvin"): "K = C + 273.15",
            ("kelvin", "celsius"): "C = K - 273.15",
            ("fahrenheit", "kelvin"): "K = (F - 32) × 5/9 + 273.15",
            ("kelvin", "fahrenheit"): "F = (K - 273.15) × 9/5 + 32"
        }
        
        key = (from_unit.lower(), to_unit.lower())
        return formulas.get(key, f"{from_unit} → {to_unit}")
    
    def _detect_category(self, from_unit: str, to_unit: str) -> Optional[str]:
        """自动检测单位类别"""
        for category, units in self.conversions.items():
            if from_unit in units and to_unit in units:
                return category
        return None


# 注册计算工具的便捷函数
def register_compute_tools():
    """注册所有计算工具"""
    from app.tools.manager import tool_manager
    
    tools = [
        MathCalculatorTool(),
        StatisticsCalculatorTool(),
        UnitConverterTool()
    ]
    
    for tool in tools:
        tool_manager.register_tool(tool)
    
    logger.info("计算工具注册完成")


# 导出
__all__ = [
    "MathCalculatorTool",
    "StatisticsCalculatorTool",
    "UnitConverterTool",
    "register_compute_tools",
]